#ifndef _MAT_H_
#define _MAT_H_

#include <cstring>

template <typename T>
struct Mat
{
	static const unsigned COLS = 3;
	static const unsigned ROWS = 4;

	T m[COLS*ROWS];
	T & tx;
	T & ty;
	T & tz;
	T & sx;
	T & sy;
	T & sz;

	Mat()
		: tx(m[TX]), ty(m[TY]), tz(m[TZ]),
		sx(m[SX]), sy(m[SY]), sz(m[SZ])
	{
		Ident();
	}
	
	Mat(const Mat &mat)
		: tx(m[TX]), ty(m[TY]), tz(m[TZ]),
		sx(m[SX]), sy(m[SY]), sz(m[SZ])
	{
		*this = mat;
	}

	Mat(const Mat &mat0, const Mat &mat1)
		: tx(m[TX]), ty(m[TY]), tz(m[TZ]),
		sx(m[SX]), sy(m[SY]), sz(m[SZ])
	{
		*this = mat0;
		*this *= mat1;
	}
	
	Mat(const T m0[])
		: tx(m[TX]), ty(m[TY]), tz(m[TZ]),
		sx(m[SX]), sy(m[SY]), sz(m[SZ])
	{
		Memcpy(m0);
	}

	Mat& operator=(const Mat &mat)
	{
		Memcpy(mat.m);
		return *this;
	}

	void Memcpy(const T m0[])
	{
		std::memcpy(m, m0, COLS*ROWS*sizeof(T));
		return *this;
	}

	Mat& operator*=(const Mat &mat)
	{
		T product[COLS*ROWS];
		for (unsigned r = 0; r < ROWS; ++r)
		{
			for (unsigned c = 0; c < COLS; ++c)
			{
				product[r*COLS + c] = 0;
				for (unsigned i = 0; i < COLS; ++i)
				{
					product[r*COLS + c] += 
						m[r*COLS + i] 
						* mat.m[i*COLS + c];
				}
			}
		}
		Memcpy(product);
		return *this;
	}

	Mat& Ident()
	{
		std::memset(m, 0, COLS*ROWS*sizeof(T));
		for (unsigned r = 0; r < COLS && r < ROWS; ++r)
		{
			m[r*COLS + r] = 1;
		}
		return *this;
	}

	Mat& Translate(T x, T y, T z)
	{
		m[TX] += x;
		m[TY] += y;
		m[TZ] += z;
		return *this;
	}

	Mat& Scale(T scalex, T scaley, T scalez)
	{
		m[SX] *= scalex;
		m[SY] *= scaley;
		m[SZ] *= scalez;
		m[TX] *= scalex;
		m[TY] *= scaley;
		m[TZ] *= scalez;
		return *this;
	}

	T TransformX(T x, T y) const
	{
		return x*m[SX] + y*m[NSINZ] + m[TX];
	}
	
	T TransformY(T x, T y) const
	{
		return x*m[SINZ] + y*m[SY] + m[TY];
	}

	enum
	{
		SX,
		SY = COLS+1,
		SZ = SY+COLS+1
	};
	enum
	{
		TX = COLS*(ROWS-1),
		TY,
		TZ
	};
	enum
	{
		COSX0 = SY,
		SINX,
		NSINX = COSX0+COLS,
		COSX1
	};
	enum
	{
		COSY0,
		NSINY = 2,
		SINY = COLS*2,
		COSY1 = SINY + 2
	};
	enum
	{
		COSZ0,
		SINZ,
		NSINZ = COLS,
		COSZ1
	};
};

typedef Mat<float> Matf;
#endif
